/*
 * Copyright (c) 2005, 2014 vacoor
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 */
package org.vacoor.nothing.security.entity;

import org.vacoor.nothing.common.validation.Groups;

import javax.persistence.Entity;
import javax.persistence.Table;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Pattern;
import javax.validation.constraints.Size;
import java.util.Date;

/**
 * 定时作业
 *
 * @author vacoor
 */
@Entity
@Table(name = "T_JOB")
public class Job extends BaseEntity {
    /* **************************
     *    Target 表达式正则
     * **************************/
    private static final String IDENTIFIER_PATTERN = "[$_a-zA-Z]+[$_a-zA-Z0-9]*";   // 标识符模式
    private static final String PACKAGE_PATTERN = IDENTIFIER_PATTERN + "+(?:." + IDENTIFIER_PATTERN + ")*"; // 包模式
    public static final String STATIC_TARGET_PATTERN = "^@(" + PACKAGE_PATTERN + ")@(" + IDENTIFIER_PATTERN + ")$";    // 静态方法模式, @fullQualifierName@method
    public static final String BEAN_TARGET_PATTERN = "^#(" + IDENTIFIER_PATTERN + ")?(?:!(" + PACKAGE_PATTERN + "))?\\.(" + IDENTIFIER_PATTERN + ")$"; // 实例方法模式, #[bean][!fullQualifierName].method
    public static final String TARGET_PATTERN = "^(?:@" + PACKAGE_PATTERN + "@" + IDENTIFIER_PATTERN + "|#(?:" + IDENTIFIER_PATTERN + ")?(?:!" + PACKAGE_PATTERN + ")?\\." + IDENTIFIER_PATTERN + ")$";

    /* ************************
     *    Cron 表达式正则模式
     * ************************/
    private static final String S_PART = "(?:[0-5]?[0-9])";   // 60进制片段
    private static final String H_PART = "(?:[0-1]?[0-9]|2[0-3])";          // 0 ~ 23
    private static final String D_PART = "(?:[0-2]?(?!0)[0-9]|^3[0-1])";    // 1 ~ 31
    private static final String M_PART = "(?:0?[0-9]|1[0-1]|JAN|FEB|MAR|APR|MAY|JUN|JUL|AUG|SEP|OCT|NOV|DEC)";  // 0 ~ 11
    private static final String W_PART = "(?:[1-7]|SUN|MON|TUE|WED|THU|FRI|SAT)";
    private static final String Y_PART = "(?:(?:19|[2-9][0-9])[0-9]{2})"; // 1900~9999
    private static final int P_ALL = 1;         // * 全部
    private static final int P_RANGE = 1 << 1;  // - 区间执行
    private static final int P_PERIOD = 1 << 2; // / 周期执行
    private static final int P_ENUM = 1 << 3;   // , 枚举
    private static final int P_NONE = 1 << 4;   // ? 不指定
    private static final int P_LAST = 1 << 5;   // L 最后
    private static final int P_CLOSE = 1 << 6;  // W 离某天最近的
    private static final int P_NO = 1 << 7;     // # 序号

    public static final String SECOND = buildStaticPattern(S_PART, P_ALL | P_RANGE | P_PERIOD | P_ENUM);
    public static final String MINUTE = SECOND;
    public static final String HOUR = buildStaticPattern(H_PART, P_ALL | P_RANGE | P_PERIOD | P_ENUM);
    public static final String DAY = buildStaticPattern(D_PART, P_ALL | P_RANGE | P_PERIOD | P_ENUM | P_NONE | P_LAST | P_CLOSE);
    public static final String MONTH = buildStaticPattern(M_PART, P_ALL | P_RANGE | P_PERIOD | P_ENUM);
    public static final String WERK = buildStaticPattern(W_PART, P_ALL | P_RANGE | P_PERIOD | P_ENUM | P_NONE | P_LAST | P_CLOSE);
    public static final String YEAR = buildStaticPattern(Y_PART, P_ALL | P_RANGE | P_PERIOD | P_ENUM);
    // cron 表达式正则
    // public static final String CRON_PATTERN = "^" + SECOND + "\\s" + MINUTE + "\\s" + HOUR + "\\s" + DAY + "\\s" + MONTH + "\\s" + WERK + "\\s" + YEAR + "$";
    public static final String CRON_PATTERN = "^(?:\\*|(?:[0-5]?[0-9])-(?:[0-5]?[0-9])|(?:[0-5]?[0-9])\\/(?:[0-5]?[0-9])|(?:[0-5]?[0-9])(?:,(?:[0-5]?[0-9]))*)\\s(?:\\*|(?:[0-5]?[0-9])-(?:[0-5]?[0-9])|(?:[0-5]?[0-9])\\/(?:[0-5]?[0-9])|(?:[0-5]?[0-9])(?:,(?:[0-5]?[0-9]))*)\\s(?:\\*|(?:[0-1]?[0-9]|2[0-3])-(?:[0-1]?[0-9]|2[0-3])|(?:[0-1]?[0-9]|2[0-3])\\/(?:[0-1]?[0-9]|2[0-3])|(?:[0-1]?[0-9]|2[0-3])(?:,(?:[0-1]?[0-9]|2[0-3]))*)\\s(?:\\*|(?:[0-2]?(?!0)[0-9]|^3[0-1])-(?:[0-2]?(?!0)[0-9]|^3[0-1])|(?:[0-2]?(?!0)[0-9]|^3[0-1])\\/(?:[0-2]?(?!0)[0-9]|^3[0-1])|(?:[0-2]?(?!0)[0-9]|^3[0-1])(?:,(?:[0-2]?(?!0)[0-9]|^3[0-1]))*|\\?|L|W)\\s(?:\\*|(?:0?[0-9]|1[0-1]|JAN|FEB|MAR|APR|MAY|JUN|JUL|AUG|SEP|OCT|NOV|DEC)-(?:0?[0-9]|1[0-1]|JAN|FEB|MAR|APR|MAY|JUN|JUL|AUG|SEP|OCT|NOV|DEC)|(?:0?[0-9]|1[0-1]|JAN|FEB|MAR|APR|MAY|JUN|JUL|AUG|SEP|OCT|NOV|DEC)\\/(?:0?[0-9]|1[0-1]|JAN|FEB|MAR|APR|MAY|JUN|JUL|AUG|SEP|OCT|NOV|DEC)|(?:0?[0-9]|1[0-1]|JAN|FEB|MAR|APR|MAY|JUN|JUL|AUG|SEP|OCT|NOV|DEC)(?:,(?:0?[0-9]|1[0-1]|JAN|FEB|MAR|APR|MAY|JUN|JUL|AUG|SEP|OCT|NOV|DEC))*)\\s(?:\\*|(?:[1-7]|SUN|MON|TUE|WED|THU|FRI|SAT)-(?:[1-7]|SUN|MON|TUE|WED|THU|FRI|SAT)|(?:[1-7]|SUN|MON|TUE|WED|THU|FRI|SAT)\\/(?:[1-7]|SUN|MON|TUE|WED|THU|FRI|SAT)|(?:[1-7]|SUN|MON|TUE|WED|THU|FRI|SAT)(?:,(?:[1-7]|SUN|MON|TUE|WED|THU|FRI|SAT))*|\\?|L|W)\\s(?:\\*|(?:(?:19|[2-9][0-9])[0-9]{2})-(?:(?:19|[2-9][0-9])[0-9]{2})|(?:(?:19|[2-9][0-9])[0-9]{2})\\/(?:(?:19|[2-9][0-9])[0-9]{2})|(?:(?:19|[2-9][0-9])[0-9]{2})(?:,(?:(?:19|[2-9][0-9])[0-9]{2}))*)$";

    /**
     * 定时作业状态
     */
    public enum JobState {
        WAIT,       // 等待执行
        RUNNING,    // 正在执行
        PAUSE,      // 暂停
        STOP        // 停止
    }

    @NotNull(groups = {Groups.C.class, Groups.U.class})
    @Size(min = 1, max = 255, groups = {Groups.C.class, Groups.U.class})
    private String name;        // 作业名称
    private String desc;        // 作业描述

    @NotNull(groups = {Groups.C.class, Groups.U.class})
    @Size(min = 1, max = 255, groups = {Groups.C.class, Groups.U.class})
    @Pattern(regexp = TARGET_PATTERN, groups = {Groups.C.class, Groups.U.class})
    private String target;      // 作业目标

    @NotNull(groups = {Groups.C.class, Groups.U.class})
    @Size(min = 1, max = 255, groups = {Groups.C.class, Groups.U.class})
    @Pattern(regexp = CRON_PATTERN, flags = Pattern.Flag.CASE_INSENSITIVE, groups = {Groups.C.class, Groups.U.class})
    private String cron;        // cron 表达式

    private Boolean concurrent; // 是否允许并发执行, false 则上一次执行未结束时下一次执行等待
    private JobState jobState;  // 作业状态
    private Date lastExecTime;  // 最后执行时间
    private Date nextExecTime;  // 下次执行时间
    private Long count;         // 执行次数
    private Long failCount;     // 失败次数

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getDesc() {
        return desc;
    }

    public void setDesc(String desc) {
        this.desc = desc;
    }

    public String getTarget() {
        return target;
    }

    public void setTarget(String target) {
        this.target = target;
    }

    public String getCron() {
        return cron;
    }

    public void setCron(String cron) {
        this.cron = cron;
    }

    public Boolean getConcurrent() {
        return concurrent;
    }

    public void setConcurrent(Boolean concurrent) {
        this.concurrent = concurrent;
    }

    public JobState getJobState() {
        return jobState;
    }

    public void setJobState(JobState jobState) {
        this.jobState = jobState;
    }

    public Date getLastExecTime() {
        return lastExecTime;
    }

    public void setLastExecTime(Date lastExecTime) {
        this.lastExecTime = lastExecTime;
    }

    public Date getNextExecTime() {
        return nextExecTime;
    }

    public void setNextExecTime(Date nextExecTime) {
        this.nextExecTime = nextExecTime;
    }

    public Long getCount() {
        return count;
    }

    public void setCount(Long count) {
        this.count = count;
    }

    public Long getFailCount() {
        return failCount;
    }

    public void setFailCount(Long failCount) {
        this.failCount = failCount;
    }

    /**
     */
    private static String buildStaticPattern(String basePattern, int flag) {
        StringBuilder buff = new StringBuilder();
        buff.append("(?:");
        if (P_ALL == (P_ALL & flag)) {
            buff.append("\\*").append("|");
        }
        if (P_RANGE == (P_RANGE & flag)) {
            buff.append(basePattern).append("-").append(basePattern).append("|");
        }
        if (P_PERIOD == (P_PERIOD & flag)) {
            buff.append(basePattern).append("\\/").append(basePattern).append("|");
        }
        if (P_ENUM == (P_ENUM & flag)) {
            buff.append(basePattern).append("(?:,").append(basePattern).append(")*").append("|");
        }
        if (P_NONE == (P_NONE & flag)) {
            buff.append("\\?").append("|");
        }
        if (P_LAST == (P_LAST & flag)) {
            buff.append("L").append("|");
        }
        if (P_CLOSE == (P_CLOSE & flag)) {
            buff.append("W").append("|");
        }
        if (P_NO == (P_NO & flag)) {
            buff.append("#").append("|");
        }
        buff.delete(buff.length() - 1, buff.length());
        buff.append(")");
        return buff.toString();
    }
}
